/// 
/// MaDAson - Toolkit for Model Driven Software Development
/// 
/// Copyright [2006] Marcus Lankenau
/// 
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///  
///     http://www.apache.org/licenses/LICENSE-2.0
///  
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
/// 

using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Reflection;

namespace Madason.MetaModel.Transformation
{
	public class Transformer
	{
		private RuleList ruleList;

		/// <summary>
		/// Rule for Transformation
		/// </summary>
		public RuleList RuleList
		{
			get { return ruleList; }
			set { ruleList = value; }
		}

		public Transformer(RuleList ruleList)
		{
			this.RuleList = ruleList;
		}

		public Transformer(XmlReader reader)
		{
			RuleList = RuleParser.Load(reader);
		}


		/// <summary>
		/// Copy alle Properties
		/// </summary>
		/// <param name="src"></param>
		/// <param name="dest"></param>
		protected void CopyProperties(object src, object dest)
		{
			Type srcType = src.GetType();
			Type destType = dest.GetType();

			foreach (PropertyInfo prop in srcType.GetProperties())
			{
				PropertyInfo destProp = destType.GetProperty(prop.Name);
				if (!prop.CanRead) continue;
				if (destProp == null) continue;
				if (!destProp.CanWrite) continue;
				if (destProp.PropertyType != prop.PropertyType) continue;

				destProp.SetValue(dest, prop.GetValue(src, null), null);
			}
		}

		protected bool EqualsIgnoreCase(string a, string b)
		{
			if (a == null || b == null) return false;
			return a.ToLower() == b.ToLower();
		}

		public void Transform(Class cls)
		{
			for (int i = 0; i < cls.Attributes.Count; i++)
			{
				Attribute attr = cls.Attributes[i];

				foreach (Rule rule in ruleList)
				{
					if (rule.Scope != RuleScope.Attribute) continue;

					if (EqualsIgnoreCase(rule.Stereotype, attr.StereotypeName) ||
						EqualsIgnoreCase(rule.ParentStereotype, attr.Class.StereotypeName))
					{

						Type t = Type.GetType(rule.Target);
						Attribute copy = t.GetConstructor(new Type[] { }).Invoke(new object[] { }) as Attribute;

						CopyProperties(attr, copy);

						// replace class
						cls.Attributes[i] = copy;
						break;
					}
				}				

			}
			for (int i = 0; i < cls.Associations.Count; i++)
			{
				Association assoc = cls.Associations[i];

				foreach (Rule rule in ruleList)
				{
					if (rule.Scope != RuleScope.Association) continue;

					if (EqualsIgnoreCase(rule.Stereotype, assoc.StereotypeName) ||
						EqualsIgnoreCase(rule.ParentStereotype, assoc.Class.StereotypeName))
					{

						Type t = Type.GetType(rule.Target);
						Association copy = t.GetConstructor(new Type[] { }).Invoke(new object[] { }) as Association;
						CopyProperties(assoc, copy);

						// replace class
						cls.Associations[i] = copy;
					}
				}

			}
			for (int i = 0; i < cls.Operations.Count; i++)
			{
				Operation op = cls.Operations[i];

				foreach (Rule rule in ruleList)
				{
					if (rule.Scope != RuleScope.Operation) continue;

					if (EqualsIgnoreCase(rule.Stereotype, op.StereotypeName) ||
						EqualsIgnoreCase(rule.ParentStereotype, op.Class.StereotypeName))
					{

						Type t = Type.GetType(rule.Target);
						Operation copy = t.GetConstructor(new Type[] { }).Invoke(new object[] { }) as Operation;
						CopyProperties(op, copy);

						// replace class
						cls.Operations[i] = copy;
					}
				}

			}
		}


		
		
		/// <summary>
		/// Transform Statemachine
		/// </summary>
		/// <param name="sm"></param>
		public void Transform(StateMachine sm)
		{
			for (int i=0; i<sm.States.Count; i++)
			{
				State state = sm.States[i];
				foreach (Rule rule in ruleList)
				{
					if (rule.Scope != RuleScope.State) continue;
					if (!EqualsIgnoreCase(rule.Stereotype, state.StereotypeName)) continue;

					Type t = Type.GetType(rule.Target);
					if (t == null)
						throw new ApplicationException("Can't find type " + rule.Target);
					State copy = t.GetConstructor(new Type[] { }).Invoke(new object[] { }) as State;					
					CopyProperties(state, copy);



					sm.States[i] = copy;
				}

			}
		}


		/// <summary>
		/// Transform Namespace Content
		/// </summary>
		/// <param name="ns"></param>
		public void Transform(Namespace ns)
		{
			for (int i = 0; i < ns.Statemachines.Count; i++)
			{
				StateMachine sm = ns.Statemachines[i];
				Transform(sm);
			}

			for (int i=0; i<ns.Classes.Count; i++)			
			{
				Class cls = ns.Classes[i];
				Transform(cls);
				foreach (Rule rule in ruleList)
				{
					if (rule.Scope != RuleScope.Class) continue;
					if (!EqualsIgnoreCase(rule.Stereotype, cls.StereotypeName)) continue;

					Type t = Type.GetType(rule.Target);					
					if (t == null)
						throw new ApplicationException("Can't find type " + rule.Target);
					Class copy = t.GetConstructor(new Type[] { }).Invoke(new object[] { }) as Class;
					CopyProperties(cls, copy);

					foreach (Attribute attr in copy.Attributes)
						attr.Class = copy;

					foreach (Operation op in copy.Operations)
						op.Class = copy;

					foreach (Association assoc in copy.Associations)
						assoc.Class = copy;


					// replace class
					ns.Classes[i] = copy;					
				}				
			}

			for (int i=0; i<ns.Namespaces.Count; i++)
			{
				Namespace child = ns.Namespaces[i];

				Transform(child);

				foreach (Rule rule in ruleList)
				{
					if (rule.Scope != RuleScope.Namespace) continue;
					if (!EqualsIgnoreCase(rule.Stereotype, child.StereotypeName)) continue;

					Type t = Type.GetType(rule.Target);
					if (t == null)
						throw new Exception("can't find type: " + rule.Target);
					ConstructorInfo ctor = t.GetConstructor(new Type[] { });
					if (ctor == null)
						throw new Exception("can't find default constructor for " + t.Name);
					Namespace copy = ctor.Invoke(new object[] { }) as Namespace;

					CopyProperties(child, copy);
					copy.TaggedValues = copy.TaggedValues;

					copy.Namespaces = child.Namespaces;
					foreach (Namespace cns in child.Namespaces)
						cns.Parent = copy;

					copy.Classes = child.Classes;
					foreach (Class cls in child.Classes)
						cls.Namespace = copy;

					ns.Namespaces[i] = copy;
				}
			}
		}

	}
}
